//@author A0105735J
#include "stdafx.h"
#include "CppUnitTest.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

// when test Receiver class, Feedback class is also being tested
// when test Inovker class, Receiver and Command class are also being tested
namespace UnitTest_logic
{		
	TEST_CLASS(UnitTest_testReceiver)
	{
	private:
		std::vector<Event*> temp;
		int temp_size;
		TimedEvent event1;
		TimedEvent event2;
		DeadlineEvent event3;
		DeadlineEvent event4;
		FloatingEvent event5;
		FloatingEvent event6;
	public:
		// test whether a new task is added
		TEST_METHOD(testReceiver_addCommand)
		{		
			StorageStub* s = new StorageStub;		
			Receiver *r = new Receiver(s);
			event1.setName("event1");
			
			Feedback output = r->addCommand(&event1);
			temp = r->getTempEvents();      
			temp_size = temp.size();
			
			Assert::AreEqual(temp_size, 1);  
			Assert::AreEqual(temp.back()->getName(),event1.getName());
			Assert::AreEqual(output.writeOutExecutionFb(), SUCCEED_ADD);
		}

		// test whether the task could be deleted in different conditions
		TEST_METHOD(testReceiver_deleteCommand)
		{
			StorageStub* s = new StorageStub;	
			Receiver *r = new Receiver(s);
			std::vector<int> id;		

			/* This is a boundary case for the invalid ID partition */
			id.push_back(3);
			Feedback output = r->deleteCommand(id);
			
			Assert::AreEqual(output.getIsValid(),false);
			Assert::AreEqual(output.writeOutExecutionFb(), ERROR_NOTFOUND);
			id.pop_back();

			/* This is a boundary case for the normal ID partition */
			r->addCommand(&event1);
			id.push_back(1);
			output = r->deleteCommand(id);
			
			Assert::AreEqual(output.getIsValid(),true);	
			Assert::AreEqual(output.writeOutExecutionFb(), SUCCEED_DEL);
		}
		// test whether could find the search target
		TEST_METHOD(testReceiver_searchCommand)
		{
			event1.setName("event 1");
			event1.setLocation("NUS");
			event2.setName("event 2");
			event2.setLocation("NTU");
			
			StorageStub* s = new StorageStub;
			Receiver *r = new Receiver(s);
			
			r->addCommand(&event1);
			r->addCommand(&event2);
			
			/* This is a boundary case for the space partition */
			//keyword contains space
			Feedback result = r->searchCommand(" ");
			
			Assert::AreEqual(result.getIsValid(),true); 
			Assert::AreEqual(result.writeOutExecutionFb(), SUCCEED_SEARCH);
			
			/* This is a boundary case for the invalid keyword partition */
			result = r->searchCommand("12345");

			Assert::AreEqual(result.getIsValid(),false); 
			Assert::AreEqual(result.writeOutExecutionFb(), ERROR_NOTFOUND);
			/* This is a boundary case for the valid search partition */
			result = r->searchCommand("event");
			temp = result.getUpdateCalendar();

			Assert::AreEqual(temp[0]->getName(), event1.getName());
			Assert::AreEqual(temp[1]->getName(), event2.getName());
			Assert::AreEqual(result.writeOutExecutionFb(), SUCCEED_SEARCH);
		}
		// test whether a the displayID all being reset
		TEST_METHOD(testReceiver_setEventsID)
		{
			/* This is a boundary case for the "invalid events to be set ID" partition */
			StorageStub* s = new StorageStub;	
			Receiver *r = new Receiver(s);
			temp = r->getTempEvents();

			r->setEventsID(temp, 1);
			Assert::AreEqual(temp.empty(), true);		
			/* This is a boundary case for the "valid events to be set ID" partition */
			DateTime d1;
			DateTime d2;
			std::string name1 = "event1";
			std::string name2 = "event2";
			std::string name3 = "event3";
			std::string name4 = "event4";
			std::string name5 = "event5";
			event1.setName(name1);
			event2.setName(name2);
			event3.setName(name3);
			event4.setName(name4);
			event5.setName(name5);
			event1.setDisplayID(1);
			event2.setDisplayID(2);
			event3.setDisplayID(3);
			event4.setDisplayID(4);
			event5.setDisplayID(5);
			d1.setDate(32,33,34);
			d1.setTime(35,36);
			d2.setDate(12,13,14);
			d2.setTime(15,16);			
			event1.setTimedEventStartTime(d1);
			event2.setTimedEventStartTime(d2);
			event3.setDeadlineEventEndtime(d1);
			event4.setDeadlineEventEndtime(d2);
			event5.setStatus(DONE);

			r->addCommand(&event1);
			r->addCommand(&event2);
			r->addCommand(&event3);
			r->addCommand(&event4);
			r->addCommand(&event5);

			temp = r->getTempEvents();
			r->setEventsID(temp, 1);			
			temp = r->getTempEvents();
			std::vector<int> setResult;

			Assert::AreEqual(temp[0]->getName(),event2.getName());    
			Assert::AreEqual(temp[1]->getName(),event1.getName());    
			Assert::AreEqual(temp[2]->getName(),event4.getName());   
			Assert::AreEqual(temp[3]->getName(),event3.getName());   
			Assert::AreEqual(temp[4]->getName(),event5.getName());  
		}
		// test whether tasks could be displayed correctly
		TEST_METHOD(testReceiver_displayCommand){
			DateTime start;
			DateTime end;
			DateTime e1, e2, e3;
			start.setDate(1,1,2014);
			end.setDate(3,1,2014);
			e1.setDate(2,1,2014);
			e2.setDate(3,1,2014);
			e3.setDate(4,1,2014);

			event1.setTimedEventStartTime(e1);
			event2.setTimedEventStartTime(e2);
			event3.setDeadlineEventEndtime(e3);			
			StorageStub* s = new StorageStub;
			Receiver *r = new Receiver(s);

			r->addCommand(&event1);
			r->addCommand(&event2);
			r->addCommand(&event3);
			/* This is a boundary case for the "valid start and end time" partition */
			Feedback output = r->displayCommand(start,end);		
			temp = output.getUpdateCalendar();
			int size = temp.size();
			
			Assert::AreEqual(size,2);
			Assert::AreEqual(output.writeOutExecutionFb(), SUCCEED_DISPLAY);

			/* This is a boundary case for the "start and end time to be same" partition */
			output = r->displayCommand(end,end);	
			temp = output.getUpdateCalendar();
			size = temp.size();
			
			Assert::AreEqual(size,1);
			
			output = r->displayCommand(start,start);	
			temp = output.getUpdateCalendar();
			size = temp.size();
			
			Assert::AreEqual(size,0);
			Assert::AreEqual(output.writeOutExecutionFb(), SUCCEED_DISPLAY);
		}
		// test whether tasks could be marked correctly
		TEST_METHOD(testReceiver_markCommand){		
			StorageStub* s = new StorageStub;
			Receiver *r = new Receiver(s);
			std::vector<int> id;		
			
			/* This is a boundary case for the invalid ID partition */
			id.push_back(3);
			Feedback output = r->markCommand(id,DONE);
			
			Assert::AreEqual(output.getIsValid(),false);
			Assert::AreEqual(output.writeOutExecutionFb(), ERROR_NOTFOUND);
			id.pop_back();
			
			/* This is a boundary case for the normal ID partition */
			event1.setStatus(PENDING);
			output = r->addCommand(&event1);
			temp = output.getUpdateCalendar();
			Assert::AreEqual(temp[0]->getEventStatus(),PENDING);
			id.push_back(1);
			output = r->markCommand(id, DONE);
			
			Assert::AreEqual(output.writeOutExecutionFb(), SUCCEED_MARK);
			Assert::AreEqual(output.getIsValid(),true);		
			
			temp = output.getUpdateCalendar();
			
			Assert::AreEqual(temp[0]->getEventStatus(),DONE);
		}
		// test whether tasks could be automatically updated status correctly
		TEST_METHOD(testReceiver_checkCommand){
			StorageStub* s = new StorageStub;
			Receiver *r = new Receiver(s);
			event1.setStatus(PENDING);
			event2.setStatus(PENDING);
			event3.setStatus(PENDING);
			DateTime e1;
			DateTime e2;
			DateTime e3;
			DateTime current;
			current = r->getCurrentTime();
			e1.setDate(current.getDay(),current.getMonth(),current.getYear());
			e1.setTime(0,0);
			e2.setDate(current.getDay()+1,current.getMonth(),current.getYear());
			e2.setTime(10,10);
			e3.setDate(current.getDay()+2,current.getMonth(),current.getYear());
			e3.setTime(10,10);
			event1.setTimedEventEndTime(e1);
			event2.setTimedEventEndTime(e2);
			event3.setDeadlineEventEndtime(e3);
			event4.setDeadlineEventEndtime(e1);
			event4.setStatus(DONE);
			
			r->addCommand(&event1);
			r->addCommand(&event2);
			r->addCommand(&event3);
			r->addCommand(&event4);
			
			Feedback output = r->checkCommand();
			temp = r->getTempEvents();
			int size = temp.size();
			
			Assert::AreEqual(temp[0]->getEventStatus(),OVERDUE);
			Assert::AreEqual(temp[1]->getEventStatus(),PENDING);
			Assert::AreEqual(temp[2]->getEventStatus(),DONE);
			Assert::AreEqual(temp[3]->getEventStatus(),PENDING);
			Assert::AreEqual(output.writeOutExecutionFb(), SUCCEED_STATUS);
		}
		// test whether a task could be updated correctly
		TEST_METHOD(testReceiver_updateCommand)
		{
			StorageStub* s = new StorageStub;
			Receiver *r = new Receiver(s);
			DeadlineEvent oldEvent;

			DateTime d1;
			DateTime d2;
			d1.setDate(32,33,34);
			d1.setTime(35,36);
			d2.setDate(12,13,14);
			d2.setTime(15,16);

			oldEvent.setDeadlineEventEndtime(d1);
			/* This is a boundary case for the "id is invalid" partition */	
			Feedback output = r->updateCommand(1,&oldEvent);
			
			Assert::AreEqual(output.getIsValid(),false);
			Assert::AreEqual(output.writeOutExecutionFb(), ERROR_NOTFOUND);

			/* This is a boundary case for the "updatedEvent has different type" partition */			
			r->addCommand(&oldEvent);
			FloatingEvent updatedEvent;
			output = r->updateCommand(1, &updatedEvent);
			temp = r->getTempEvents();
			EventType newType = temp[0]->getEventType();
			
			Assert::AreEqual((newType == FLOATING), true);
			Assert::AreEqual(output.writeOutExecutionFb(), SUCCEED_UPDATE);
			
			/* This is a boundary case for the "updatedEvent has same type" partition */
			oldEvent.setName("old");
			r->addCommand(&oldEvent);

			DeadlineEvent newEvent;
			newEvent.setName("new");
			output = r->updateCommand(1,&newEvent);
			temp = r->getTempEvents();
			
			Assert::AreEqual(temp[0]->getName(), newEvent.getName());
			Assert::AreEqual(output.writeOutExecutionFb(), SUCCEED_UPDATE);
		}	
	};

	TEST_CLASS(UnitTest_testInvoker)
	{
	private:
		std::vector<Event*> temp;
		int temp_size;
		TimedEvent event1;
		TimedEvent event2;
		DeadlineEvent event3;
		DeadlineEvent event4;
		FloatingEvent event5;
		FloatingEvent event6;
	public:
		// test whether a add command could be undo successfully
		TEST_METHOD(testInvoker_undoAddCommand){
			Invoker ivk;
			CommandAdd* addEventCommand = new CommandAdd;
			event1.setName("event1");
			addEventCommand->setEvent(&event1);
			ivk.storeCommand(addEventCommand);
			ivk.executeCommand();
			Feedback output = ivk.undo();
			
			Assert::AreEqual(output.writeOutExecutionFb(), SUCCEED_UNDO);
			temp = output.getUpdateCalendar();
			
			Assert::AreEqual(temp[0]->getName(), event1.getName());
		}		
		// test whether a delete command could be undo successfully
		TEST_METHOD(testInvoker_undoDeleteCommand){
			Invoker ivk;
			CommandAdd* addEventCommand = new CommandAdd;
			event1.setName("event1");
			addEventCommand->setEvent(&event1);
			ivk.storeCommand(addEventCommand);
			ivk.executeCommand();

			CommandDelete* deleteEventCommand = new CommandDelete;
			std::vector<int> delID;
			delID.push_back(1);
			deleteEventCommand->setID(delID);
			ivk.storeCommand(deleteEventCommand);
			ivk.executeCommand();			
			
			Feedback output = ivk.undo();
			
			Assert::AreEqual(output.writeOutExecutionFb(), SUCCEED_UNDO);
			temp = output.getUpdateCalendar();
			
			Assert::AreEqual(temp[0]->getName(), event1.getName());
		}
		// test whether a mark command could be undo successfully
		TEST_METHOD(testInvoker_undoMarkCommand){
			Invoker ivk;
			CommandAdd* addEventCommand = new CommandAdd;
			event1.setName("event1");
			event1.setStatus(PENDING);
			addEventCommand->setEvent(&event1);
			ivk.storeCommand(addEventCommand);
			ivk.executeCommand();

			CommandMark* markEventCommand = new CommandMark;
			std::vector<int> markID;
			markID.push_back(1);
			markEventCommand->setID(markID);
			ivk.storeCommand(markEventCommand);
			Feedback output = ivk.executeCommand();
			temp = output.getUpdateCalendar();
			Assert::AreEqual(temp[0]->getEventStatus(), DONE);

			output = ivk.undo();
			Assert::AreEqual(output.writeOutExecutionFb(), SUCCEED_UNDO);
			temp = output.getUpdateCalendar();
			Assert::AreEqual(temp[0]->getEventStatus(), PENDING);
		}
		// test whether an update command could be undo successfully
		TEST_METHOD(testInvoker_undoUpdateCommand){
			Invoker ivk;
			CommandAdd* addEventCommand = new CommandAdd;
			event1.setName("event1");
			event1.setStatus(PENDING);
			addEventCommand->setEvent(&event1);
			ivk.storeCommand(addEventCommand);
			ivk.executeCommand();

			CommandUpdate* updateEventCommand = new CommandUpdate;
			updateEventCommand->setEvent(&event3);
			updateEventCommand->setID(1);
			ivk.storeCommand(updateEventCommand);
			Feedback output = ivk.executeCommand();
			temp = output.getUpdateCalendar();
			
			Assert::AreEqual((temp[0]->getEventType() == DEADLINE), true);
			output = ivk.undo();
			
			Assert::AreEqual(output.writeOutExecutionFb(), SUCCEED_UNDO);
			temp = output.getUpdateCalendar();
			
			Assert::AreEqual((temp[0]->getEventType() == TIME), true);			
		}
	};
}